package com.hp.provider.config;

import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.ConfigUtils;
import org.apache.dubbo.rpc.*;
import org.apache.dubbo.validation.Validation;
import org.apache.dubbo.validation.Validator;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import java.util.HashMap;
import java.util.Set;

import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER;
import static org.apache.dubbo.common.constants.FilterConstants.VALIDATION_KEY;

/**
 * 自定义 dubbo 参数验证
 * <p>
 * 注:
 * ① 如果这边 指定了 value 值，必须 在 配置文件中 ，显性的指定 filter 的名字。否则不要指定。就会默认加载 这个 filter
 *
 * @author <a href="mailto:xing.dong@sha.ontime-express.com">Xavier Dong</a>
 * @date
 */
//@Activate(group = {CONSUMER, PROVIDER}, value = "customValidationFilter", order = 10000)
@Activate(group = {CONSUMER, PROVIDER}, order = 10000)
public class CustomValidationFilter implements Filter {

    private Validation validation;

    public void setValidation(Validation validation) {
        this.validation = validation;
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        if (validation != null && !invocation.getMethodName().startsWith("$")
                && ConfigUtils.isNotEmpty(invoker.getUrl().getMethodParameter(invocation.getMethodName(), VALIDATION_KEY))) {
            try {
                Validator validator = validation.getValidator(invoker.getUrl());
                if (validator != null) {
                    validator.validate(invocation.getMethodName(), invocation.getParameterTypes(), invocation.getArguments());
                }
            } catch (RpcException e) {
                throw e;
            } catch (ConstraintViolationException e) {
                // 这边细化了异常类型
                // 新增的针对特定异常ConstraintViolationException的处理
                Set<ConstraintViolation<?>> violations = e.getConstraintViolations();
                if (CollectionUtils.isNotEmpty(violations)) {
                    // 取第一个进行提示就行了
                    final HashMap<Object, Object> collect = violations.stream().collect(HashMap::new, (k, v) -> k.put(v.getPropertyPath().toString(), v.getMessage()), HashMap::putAll);
                    //ConstraintViolation<?> violation = violations.iterator().next();
                    //final com.hp.common.common.Result<Object> result = com.hp.common.common.Result.createByErrorCodeMessage(com.hp.common.common.Result.ResponseCode.BAD_REQUEST.getCode(), violation.getMessage());
                    final com.hp.common.common.Result<HashMap<Object, Object>> mapResult = com.hp.common.common.Result.createByError(com.hp.common.common.Result.ResponseCode.BAD_REQUEST.getCode(), collect);
                    return AsyncRpcResult.newDefaultAsyncResult(mapResult, invocation);
                }
                return AsyncRpcResult.newDefaultAsyncResult(new ValidationException(e.getMessage()), invocation);
            } catch (Throwable t) {
                return AsyncRpcResult.newDefaultAsyncResult(t, invocation);
            }
        }
        return invoker.invoke(invocation);
    }
}